#!/usr/bin/env python3

# Copyright (c) 2020-2025 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import re
import pathlib
from .._handy import escape_unit_name, unescape_unit_name
from ._service import Service


class SwapService(Service):

    # FIXME: does not consider the case that service file is a dangling symlink which means remove this service

    @staticmethod
    def _name2UnitName(name):
        return name + ".swap"

    @classmethod
    def _create(cls, parent, dir_path, name=None, unit_name=None, swap_path=None):
        unit_name, swap_path = cls._processArgs(name, unit_name, swap_path)
        buf = cls._generateSwapServiceFileContent(None, swap_path)
        pathlib.Path(os.path.join(dir_path, unit_name)).write_text(buf)
        return cls(parent, unit_name=unit_name)

    @classmethod
    def _exists(cls, parent, dir_path, name=None, unit_name=None, swap_path=None):
        unit_name, swap_path = cls._processArgs(name, unit_name, swap_path)
        buf = cls._generateSwapServiceFileContent(None, swap_path)
        fullfn = os.path.join(dir_path, unit_name)
        if not os.path.exists(fullfn):
            return None
        if pathlib.Path(fullfn).read_text() != buf:
            return None
        return cls(parent, unit_name=unit_name)

    def __init__(self, parent, name=None, unit_name=None, swap_path=None):
        if name is not None:
            assert unit_name is None and swap_path is None
            super().__init__(parent, name + ".swap")
            self._swapPath = self._name2SwapPath(self._name)
        elif unit_name is not None:
            assert unit_name.endswith(".swap")
            assert swap_path is None
            super().__init__(parent, unit_name)
            self._swapPath = self._unitName2SwapPath(self._unitName)
        elif swap_path is not None:
            super().__init__(parent, escape_unit_name(swap_path[1:]) + ".swap")
            self._swapPath = swap_path
        else:
            assert False

    @property
    def swap_path(self):
        return self._swapPath

    def remove_and_dispose(self):
        super().remove_and_dispose()
        del self._swapPath

    @classmethod
    def _processArgs(cls, name, unit_name, swap_path):
        if name is not None:
            assert unit_name is None and swap_path is None
            unit_name = cls._name2UnitName(name)
            swap_path = cls._name2SwapPath(name)
            return (unit_name, swap_path)

        if unit_name is not None:
            assert unit_name.endswith(".swap")
            assert swap_path is None
            swap_path = cls._unitName2SwapPath(unit_name)
            return (unit_name, swap_path)

        if swap_path is not None:
            unit_name = escape_unit_name(swap_path[1:]) + ".swap"
            return (unit_name, swap_path)

        assert False

    @staticmethod
    def _generateSwapServiceFileContent(description, swapPath):
        buf = ""
        buf += "[Unit]\n"
        if description is not None:
            buf += description + "\n"
        else:
            if re.fullmatch("/dev/ram[0-9]+", swapPath):
                buf += "Description=Compressed Memory\n"
            elif swapPath.startswith("/dev/"):
                buf += "Description=Swap Partition\n"
            else:
                buf += "Description=Swap File\n"
        buf += "\n"
        buf += "[Swap]\n"
        buf += "What=%s\n" % (swapPath)
        buf += "\n"
        buf += "[Install]\n"
        buf += "WantedBy=swap.target\n"
        return buf

    @staticmethod
    def _name2SwapPath(name):
        path = unescape_unit_name(name)
        path = os.path.join("/", path)
        return path

    @classmethod
    def _unitName2SwapPath(cls, unitName):
        # item[:-5] is to remove ".swap"
        return cls._name2SwapPath(unitName[:-5])
